#pragma once

#include <string>
#include <functional>
#include "Log.h"
#include "Intaddr.h"
#include "Common.h"
#include "Thread.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>

namespace zmh
{
    // 服务器一般是不可拷贝的,甚至有些环境下应该是单例模式
    class serve : public nocopy
    {
        typedef std::function<void(std::string, int, const Intaddr &)> handler;

        void service(int fd, Intaddr peer)
        {
            while (true)
            {
                // 收到数据
                char buffer[1024] = {0};
                int n = read(fd, buffer, sizeof(buffer) - 1);
                if (n == -1)
                {
                    // 读取错误
                    LOG(LogLevel::ERROR) << "read mistake:" << strerror(errno);
                    break;
                }
                else if (n == 0)
                {
                    // 读到EOF
                    LOG(LogLevel::INFO) << "Read the end of the file EOF";
                    break;
                }
                buffer[n] = 0;
                // 处理数据
                _func(buffer, fd, peer);
                // 记录日志
                LOG(LogLevel::INFO) << "client-" << peer.Name() << "-say: " << buffer;
            }
        }

    public:
        serve(const Intaddr &addr, const handler &func)
            : _addr(addr),
              _isrunning(false),
              _func(func)
        {
            // step1 --- 使用Tcp协议(传输层控制协议、有连接、可重发、面向字节流)打开监听套接字
            _lfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_lfd == -1)
            {
                LOG(LogLevel::FATAL) << "socket mistake: " << strerror(errno);
                exit(Err_Socket);
            }
            LOG(LogLevel::INFO) << "socket success";
            // step2 --- 进行IP、PORT绑定 (网络数据默认大端存储)
            if (bind(_lfd, (const struct sockaddr *)&_addr.Addr(), sizeof(_addr.Addr())) == -1)
            {
                LOG(LogLevel::FATAL) << "bind mistake:" << strerror(errno);
                exit(Err_Bind);
            }
            LOG(LogLevel::INFO) << "bind success";
            // step3 --- 将服务器设置为监听状态
            if ((listen(_lfd, 8)) == -1) // 将套接字_lfd设置为监听,且允许排队数为8
            {
                LOG(LogLevel::FATAL) << "listen mistake:" << strerror(errno);
                exit(Err_Listen);
            }
            LOG(LogLevel::INFO) << "listen success";
        }
        void start()
        {
            _isrunning = true;
            while (_isrunning)
            {
                // 获取连接
                struct sockaddr_in peer;
                socklen_t peerlen = sizeof(peer);
                int fd = accept(_lfd, (struct sockaddr *)&peer, &peerlen);
                if (fd == -1)
                {
                    LOG(LogLevel::WARNING) << "accept mistake:" << strerror(errno);
                    continue;
                }
                LOG(LogLevel::INFO) << Intaddr(peer).Name() << " accept sucess";
                // 多线程
                zmh::Thread t(&serve::service, this, fd, Intaddr(peer));
                t.detach();
            }
            _isrunning = false;
        }

    private:
        int _lfd;        // 文件描述符
        Intaddr _addr;   // socket
        bool _isrunning; // 服务器的状态 --- true表运行
        handler _func;   // 回调函数 --- 服务器如何处理数据
    };
}

// 存在一个问题：当客户端正在连接服务器的时候，服务器之间退出或关键，“立即”重新启动服务器会出现以下问题:
// [2025-05-06-20:02:47] [FATAL] [1513449] [serve.h] [64] bind mistake:Address already in use
// 当前的理解是: 当服务器关闭时，网络连接不会立即消失，系统会保留一小段时间(通常1-2分钟)
// 挂电话后不是立即能重拨，需要等一小会儿
// // 多进程版本
// pid_t pd = fork();
// if (pd == -1)
// {
//     LOG(LogLevel::FATAL) << "fork mistake:" << strerror(errno);
//     break;
// }
// else if (pd == 0)
// {
//     close(_lfd);
//     pid_t pd = fork();
//     if (pd == 0)
//         service(fd, Intaddr(peer));
//     exit(0);
// }
// else
// {
//     // 父进程执行流
//     // 但父进程应该不需要进行阻塞等待的,问题是,确保子进程服务完毕后内存完成回收
//     // 1. 非阻塞等待 --- 万一服务器卡在accept会造成内存泄露
//     // 2. 通过信号对子进程退出进行忽略处理
//     // 3. 让子进程创建孙子进程,直接退出
//     close(fd);
//     waitpid(pd, nullptr, 0);
// }
// // 单进程版本
// service(fd, Intaddr(peer));
// _ptr_tp(zmh::ThreadPool<std::function<void()>>::GetHandler(8)) // 线程池总会用完,线程池适合做短服务
